# This class is responsible for creating haplo tables.
# This class is responsible for the graphical presentation, and uses specific parsers in order to actualy parse the tool's output.
package HaplotypeWrapper;
use strict;
use Class::Struct;
use Math::BigFloat;
use DatWrapper;
use PedWrapper;
use HmmOutputParser; 
use HaplotypingOutputParser; 
use MorganOutputParser; 
use FamilyOrganaizer;
use CssCreator;

our $HMM_TOOL = "HMM";
our $MORGAN_TOOL     = "MORGAN";
our $HAPLOTYPING_TOOL    = "HAPLOTYPING";

# Constructor
# Input:
# 1.	The name of the haplotyping tool that was used
# 2.	A wrapper to the dat file (DatWrapper)
# 3.	A wrapper to the ped file (PedWrapper)
# 4.	The number of the current family
# 5.	Possibly: The name of a translate file that translates person numbers to names.
sub new( $ ){
     my ($class, $haploTool, $datWrapper, $pedWrapper, $familyNumber, $translateFile) = @_;	
		
	# create specific parser
	my $parser;
	if ($haploTool eq $HMM_TOOL){
		$parser = new HmmOutputParser();	
	}
	else { if ($haploTool eq $MORGAN_TOOL){
		$parser = new MorganOutputParser();	
	}
	else { if ($haploTool eq $HAPLOTYPING_TOOL){
		$parser = new HaplotypingOutputParser();	
	}
	else { 
		die "haploTool \"$haploTool\" is not supported";
	}}}
	
    my $self = {
		_parser       => $parser,
		_datWrapper   => $datWrapper,
		_pedWrapper   => $pedWrapper,
		_familyNumber => $familyNumber,
		_cssCreator   => new CssCreator(), #create table style
		_toolOutputTable =>[], # table with tool output
		_allelesTable  =>[],   # table with actual alleles
		_familyOrganizer => new FamilyOrganaizer($pedWrapper, $familyNumber),# create FamilyOrganaizer that will help in family sorting
	};		
	bless $self, $class;
	$self->_loadTranslatedPedigree($translateFile);# load the real names of the family members
    return $self;
}

# Load the name translates.
# Translate file should contain a separate line for each family member. Each line looks like this:
# 			<family_number>		<family_member_number>		<family_memeber_real_name>
# If the translate file doesn't exist- for each person n his name will be "Person n"
sub _loadTranslatedPedigree(){
	my ($self, $translateFile ) = @_;
	
	my $pedWrapper = $self->{_pedWrapper};
	my $currFamiliy = $self->{_familyNumber};
	
	# Initialize default names ("Person n")
	for my $personNum (keys %{$pedWrapper->{FAMILIES}{$currFamiliy}}) {
		$self->{TRANSLATED_PEDIGREE}{$personNum} =  "Person $personNum";
	}
	
	# if translate file exist- load the real names
	if (defined $translateFile && -e $translateFile){
		open T, $translateFile;
		my @arr = <T>;
		close(T);
		# for each person
		foreach my $line (@arr) {
			# read translated data. $val[0]=family, $val[1]=personID, $val[2]=real name
			my @val = split(/\s+/,$line);
			if ($val[0] eq $currFamiliy){
				$self->{TRANSLATED_PEDIGREE}{$val[1]} =  "$val[2] ($val[1])";
			}
		}
	}
}

# Read output window and add it to output table.
# Input:
# 1.	The name of the current output file
# 2.	Number of current window (numers should be continus, starting from 1)
# 2.	List of markers that participate in current window (not neccesery continuous)
sub readWindowToolOutput{
	my ($self, $outputFile, $windowNumber, $currentMarkers) = @_;
		
	#input validations
	-e $outputFile || die "The file: $outputFile does not exist";
	
	#parse input
	$self->{_parser}->parseOutput($self->{_datWrapper}, $self->{_pedWrapper}, $outputFile, $currentMarkers, $self->{_familyNumber});
	
	#add the parsed data to output table
	if ($windowNumber == 1){
		$self->_createHeader($self->{_toolOutputTable});#if this is first window- create table header
	}
	else{
		$self->_createGrayRow($self->{_toolOutputTable});#create gray line in top of the window
	}
	$self->_createWindow($self->{_toolOutputTable}, $currentMarkers, $windowNumber, "LABELS");#create window
}
# Read alleles window and add it to alleles table.
# Input:
# 1.	The name of the current alleles file
# 2.	Number of current window (numers should be continus, starting from 1)
# 2.	List of markers that participate in current window (not neccesery continuous)
sub readWindowAlleles{
	my ( $self, $allelesFile, $windowNumber, $currentMarkers) = @_;
			
	#input validations
	-e $allelesFile || die "The file: $allelesFile does not exist";
	
	#parse input
	$self->{_parser}->parseAlleles($allelesFile);
	
	#add the parsed data to alleles table
	if ($windowNumber == 1){
		$self->_createHeader($self->{_allelesTable});#if this is first window- create table header
	}
	else{
		$self->_createGrayRow($self->{_allelesTable});#create gray line in top of the window
	}
	$self->_createWindow($self->{_allelesTable}, $currentMarkers, $windowNumber, "ALLELES");#create window
}

# return the table that contains the tool output (labels table)
sub getHaplotypingTable{
	my ($self) = @_;
	return $self->{_toolOutputTable};
}

# return the table that contains the alleles
sub getAllelesTable{
	my ($self) = @_;
	return $self->{_allelesTable};
}
# Helper function- creates a table cell.
# This cell can be used in HTMLCreator
sub _createCell{
	my ($value, $isHeader, $style, $class) = @_;
	my $cell = {};
	$cell->{VALUE} = $value;#cell value
	if ($isHeader eq "true"){
		$cell->{IS_HEADER} = "true";#is header
	}
	$cell->{STYLE} = $style;#cell style
	if (not($class eq "")){
		$cell->{CSS_CLASS} = $class;#css class
	}
	
	return $cell;
}

# Create an empty gray line in the end of the table.
# This function is used in order to draw a separator between different windows
# Input: the table where line should be added
sub _createGrayRow(){
	my ($self, $table) = @_;
	my $pedWrapper   = $self->{_pedWrapper};
	my $familyNumber = $self->{_familyNumber};
	my @grayRow = [];
	#create gray cell
	my @familyMembers = $pedWrapper->getFamilyMembers($familyNumber);
	#number of columns is ((2*num_of_family_members)+num_of_prefix_columns)
	my $numberOfColumns = (2*($#familyMembers+1)) + 3;
	my $grayCell = _createCell("", "false", "colspan=\"".$numberOfColumns."\"", "grayRowStyle");
	push(@grayRow, $grayCell);

	#add gray row to the table
	push(@{$table}, \@grayRow);	
}
# Return the class for a person "secondCell"\"lastTyped"\"lastAffected"
# This is used in order to draw a different border between people with different categories 
# Input: person name
sub getCssClassStyleForAPerson{
	my ($self, $personNum) = @_;
	my $familyOrganaizer = $self->{_familyOrganizer};
	
	my $borderClass = "secondCell";
	if ($familyOrganaizer->isLastTyped($personNum) eq "true"){
		$borderClass = "lastTyped";
	}
	if ($familyOrganaizer->isLastAffected($personNum) eq "true"){
		$borderClass = "lastAffected";
	}
	return $borderClass;
}

# Create a header to the table
# This function is used in order to draw a header on top of the table
# Input: the table where header should be added
sub _createHeader(){
	my ($self, $table) = @_;
	my $pedWrapper   = $self->{_pedWrapper};
	my $familyNumber = $self->{_familyNumber};	
	my $familyOrganaizer = $self->{_familyOrganizer};
	
	#ROW 1
	my @affectionRow = [];
	#create headers of 2 first columns
	my $windowCell = _createCell("Window", "true", "rowspan=\"3\"", "firstColumn");
	my $markerNameCell = _createCell("Marker Name", "true", "rowspan=\"3\"", "");
	my $markerPositionCell = _createCell("Marker Position", "true", "rowspan=\"3\"", "");
	push(@affectionRow, $windowCell);
	push(@affectionRow, $markerNameCell);
	push(@affectionRow, $markerPositionCell);
	
	#create affection headers (Affected/Un-Affected/Unknown)
	#use familyOrganaizer for deciding the size of each header
	my $numOfAffected = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::AFFECTED_STATUS)*2;
	my $numOfUnAffected = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::UN_AFFECTED_STATUS)*2;
	my $numOfUnknown = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::UNKNOWN_STATUS)*2;
	($numOfAffected > 0) && push(@affectionRow, _createCell("Affected", "true", "colspan=\"$numOfAffected\"", "lastAffected"));
	($numOfUnAffected > 0) && push(@affectionRow, _createCell("Un-Affected", "true", "colspan=\"$numOfUnAffected\"", "lastAffected"));
	($numOfUnknown > 0) && push(@affectionRow, _createCell("Unknown", "true", "colspan=\"$numOfUnknown\"", "lastAffected"));
	push(@{$table}, \@affectionRow); 
	
	#ROW 2
	my @typedRow = [];
	#create typed headers (typed/unTyped) under each affection group.
	#use familyOrganaizer for deciding the size of each header
	my $numOfAffectedTyped = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::AFFECTED_STATUS, "true")*2;
	my $numOfAffectedUnTyped = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::AFFECTED_STATUS, "false")*2;
	my $numOfUnAffectedTyped = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::UN_AFFECTED_STATUS, "true")*2;
	my $numOfUnAffectedUnTyped = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::UN_AFFECTED_STATUS, "false")*2;
	my $numOfUnknownTyped = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::UNKNOWN_STATUS, "true")*2;
	my $numOfUnknownUnTyped = $familyOrganaizer->getNumberOfAffected($FamilyOrganaizer::UNKNOWN_STATUS, "false")*2;
	
	my $AffectedTypedBorderClass = "lastTyped";
	my $UnAffectedTypedBorderClass = "lastTyped";
	my $UnknownTypedBorderClass = "lastTyped";
	if ($numOfAffectedUnTyped == 0)   { $AffectedTypedBorderClass = "lastAffected";}
	if ($numOfUnAffectedUnTyped == 0) { $UnAffectedTypedBorderClass = "lastAffected";}
	if ($numOfUnknownUnTyped == 0)    { $UnknownTypedBorderClass = "lastAffected";}
	
	($numOfAffectedTyped > 0) && push(@typedRow, _createCell("Typed", "true", "colspan=\"$numOfAffectedTyped\"", $AffectedTypedBorderClass));
	($numOfAffectedUnTyped > 0) && push(@typedRow, _createCell("Un-Typed", "true", "colspan=\"$numOfAffectedUnTyped\"", "lastAffected"));
	($numOfUnAffectedTyped > 0) && push(@typedRow, _createCell("Typed", "true", "colspan=\"$numOfUnAffectedTyped\"", $UnAffectedTypedBorderClass));
	($numOfUnAffectedUnTyped > 0) && push(@typedRow, _createCell("Un-Typed", "true", "colspan=\"$numOfUnAffectedUnTyped\"", "lastAffected"));
	($numOfUnknownTyped > 0) && push(@typedRow, _createCell("Typed", "true", "colspan=\"$numOfUnknownTyped\"", $UnknownTypedBorderClass));
	($numOfUnknownUnTyped > 0) && push(@typedRow, _createCell("Un-Typed", "true", "colspan=\"$numOfUnknownUnTyped\"", "lastAffected"));
	push(@{$table}, \@typedRow);	
	
	#ROW 3
	my @membersRow = [];
	#create header for each family member
	for my $personNum ($familyOrganaizer->getSortedFamilyMembers()) {
		#get person's real name
		my $personName = $self->{TRANSLATED_PEDIGREE}{$personNum};
		my $cell = _createCell("$personName", "true", "colspan=\"2\"",  $self->getCssClassStyleForAPerson($personNum));
		push(@membersRow, $cell);
	}
	push(@{$table}, \@membersRow);	
}
# Add a window to a table (this methos should be called after parsing the output)
# Input: 
#	1) a table where window should be inserted
# 	2) markers of current window,
# 	3) type of current table- ("LABELS" or "ALLELES")
sub _createWindow{
	my ($self, $table, $currentMarkers, $windowNumber, $tableType) = @_;
	my $datWrapper   = $self->{_datWrapper};
	my $cssCreator = $self->{_cssCreator};
	my @markersArr = @{$currentMarkers};
	$#markersArr >=0 || return;
	my $familyOrganaizer = $self->{_familyOrganizer};
	my $firstMarkerName = $datWrapper->getMarkerName($markersArr[0]);
	$familyOrganaizer->translateLabels($self->{_parser}, $firstMarkerName);
	
	#add window number (first column)
	my @firstRow = [];
	my $useFirstRow = "true";
	my $markerNameCell = _createCell($windowNumber, "false", "rowspan=\"".($#markersArr+1)."\" bgcolor=\"white\"", "firstColumn");
	push(@firstRow, $markerNameCell);
		
	#for each marker in current window
	for my $markerNum (@markersArr){
		my @rowCells = [];
		if ($useFirstRow eq "true"){
			@rowCells = @firstRow;
			$useFirstRow = "false";
		}
		#add marker name to table (column 2)
		my $markerName = $datWrapper->getMarkerName($markerNum);
		my $markerNameCell = _createCell($markerName, "false", "bgcolor=\"white\"", "");
		push(@rowCells, $markerNameCell);
		#add marker position to table (column 3)
		my $markerPosition = Math::BigFloat->new($datWrapper->getDistIn_cM_FromFirstMarker($markerNum));
		$markerPosition->precision(-4);#marker position is too long- cut it
		my $markerDistCell = _createCell($markerPosition, "false", "bgcolor=\"white\"", "");
		push(@rowCells, $markerDistCell);#push(@{$table}, \@rowCells);
		
		#add cell for each family member
		for my $personNum ($familyOrganaizer->getSortedFamilyMembers()) {
			#get the cell with tool's output information
			#this information is needed anyway, for cell background
			my $labelCellData = $self->{_parser}->getLabelCell($personNum, $markerName); 
			my $cellData; 
			if ($tableType eq "LABELS"){#if table is "LABELS"- draw the output information
				$cellData = $labelCellData;
			}
			else { if ($tableType eq "ALLELES"){#if table is "ALLELES"- draw the alleles information
				$cellData = $self->{_parser}->getAlleleCell($personNum, $markerName);
			}
			else{
				die "unsupported table type";
			}}
			if (not defined $cellData) { 
				#print "ERROR: parser doesn't contain information about person number $personNum, marker $markerName\n";
				#next;
				$cellData = CellData->new();
				$cellData->val1("0");
				$cellData->val2("0");
				$labelCellData = $cellData;
			}
			my $translatedLabel1 = $familyOrganaizer->getTranslatedLabel($labelCellData->val1());
			my $translatedLabel2 = $familyOrganaizer->getTranslatedLabel($labelCellData->val2());
			my $firstCell  = _createCell($cellData->val1(), "false", "bgcolor=\"".$cssCreator->getColorForLabel($translatedLabel1)."\"", "firstCell");
			my $secondCell = _createCell($cellData->val2(), "false", "bgcolor=\"".$cssCreator->getColorForLabel($translatedLabel2)."\"", $self->getCssClassStyleForAPerson($personNum));
			push(@rowCells, $firstCell);
			push(@rowCells, $secondCell);
		}		
		push(@{$table}, \@rowCells);	
	}
}
1;
